#include <llvm/ADT/ArrayRef.h>
#include <llvm/LLVMContext.h>
#include <llvm/Module.h>
#include <llvm/Function.h>
#include <llvm/BasicBlock.h>
#include <llvm/Support/IRBuilder.h>
#include <vector>
#include <string>

int main()
{
	/* creating a module */
	llvm::LLVMContext& context = llvm::getGlobalContext();
	llvm::Module* module = new llvm::Module("top", context);
	llvm::IRBuilder<> builder(context);

	/* adding main method to top module */
	llvm::FunctionType *funcType = 
		llvm::FunctionType::get(builder.getInt32Ty(), false);
	llvm::Function *mainFunc = 
		llvm::Function::Create(funcType, llvm::Function::ExternalLinkage,
															"main", module);

	/* adding a basic block to main method */
	llvm::BasicBlock *entry = 
		llvm::BasicBlock::Create(context, "entrypoint", mainFunc);
	builder.SetInsertPoint(entry);

	/* adding global string to top module */
	llvm::Value *helloWorld = builder.CreateGlobalStringPtr("hello world!\n");
	
	/* declare the puts method */
	std::vector<llvm::Type *> putsArgs;
	putsArgs.push_back(builder.getInt8Ty()->getPointerTo());
	llvm::ArrayRef<llvm::Type*> argsRef(putsArgs);

	llvm::FunctionType *putsType =
		llvm::FunctionType::get(builder.getInt32Ty(), argsRef, false);
	llvm::Constant *putsFunc = module->getOrInsertFunction("puts", putsType);

	builder.CreateCall(putsFunc, helloWorld);
	builder.CreateRetVoid();

	module->dump();
}
